﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include "GcmpDevService.h"
#include "StringUtil.h"
#include "OwnerPtr.h"
#include <cmath>
#include <cstring>
#include <vector>
#include <map>

#pragma warning (push)
#pragma warning (disable:4251)

namespace gtest
{
    class GAssertResult;
    class GTestManagerImp;
    // 出错回调函数
    typedef void (*OnFailuresFoundFunc)(const gtest::GAssertResult&);

    /// \brief 保存的文件类型
    enum class FileType : int32_t
    {
        HTML = 0,          ///< 保存html格式
        XML = 1,           ///< 保存xml格式
    };

    // 断言结果
    class GCMP_DEV_SERVICE_EXPORT GAssertResult
    {
    public:
        enum EnType 
        {
            ART_NonFatalFailure = 0,    // If failed but the test can continue.
            ART_FatalFailure            // Failed and the test should be terminated.
        };

    private:
        EnType m_type;
        bool m_success;
        std::wstring m_test;
        std::wstring m_testCase;
        std::wstring m_assert;

    public:
        GAssertResult(const GAssertResult& another);
        GAssertResult& operator = (const GAssertResult& another);

        GAssertResult(
            EnType type,
            bool success,
            const std::wstring& test,
            const std::wstring& testCase,
            const std::wstring& assert
            );

        // 断言是否成功
        bool Success(void) const { return m_success; }

        // 是否有致命失败
        bool HasFatalFailure(void) const;

        // 是否有非致命失败
        bool HasNonFatalFailure(void) const;

        // 取反语义
        GAssertResult operator !(void) const;

        // 转成std::wstring
        std::wstring ToString(void) const;

       
        /// \brief 构造获取测试名称
        ///
        /// \param 无
        /// \return 返回测试名称
        std::wstring GetTestName() const;

        /// \brief 获取测试用例名称
        ///
        /// \param 无
        /// \return 返回测试用例名称
        std::wstring GetTestCaseName() const;

        /// \brief 获取断言相关信息
        ///
        /// \param 无
        /// \return 返回断言相关信息
        std::wstring GetAssertInformation() const;
    };

    // 测试夹具基类。派生自该类来实现自己的准备和清理工作
    class GCMP_DEV_SERVICE_EXPORT GTestFixture
    {
    public:
        GTestFixture(const std::wstring& testName, const std::wstring& testCaseName);
        GTestFixture(const std::wstring& testName, const std::wstring& testCaseName, const std::wstring& tag);
        virtual ~GTestFixture(void);

        // 测试前准备。返回false表示准备不足，不继续运行Test()。
        virtual bool SetUp(void);

        // 测试后清理
        virtual void TearDown(void);

        // 返回true表示没有发生FatalFailure。
        bool RunTest(OnFailuresFoundFunc onFatalFailureFound);

        // 在断言结束时记录状态
        void OnAssertEnd(const GAssertResult& ar);

        // 获取测试名字
        const std::wstring& GetTestName(void) const;

        // 获取测试用例名字
        const std::wstring& GetTestCaseName(void) const;

        // 获取测试用例标签
        const std::wstring& GetTestCaseTag(void) const;

        // 是否有致命失败
        bool HasFatalFailure(void) const;

    protected:
        virtual void Test(void);
        std::wstring m_testCaseName;
        std::wstring m_testName;
        std::wstring m_tag;
        bool m_hasFatalFailure;
        OnFailuresFoundFunc m_onFatalFailureFound;
    };

    // 测试管理器
    class GCMP_DEV_SERVICE_EXPORT GTestManager
    {
    public:
        static GTestManager * GetInstance(void);

        // 重置状态。在运行所有的测试前需要调用
        void Reset(void);

        // 传入的pTestFixture不应为空，为空返回false
        bool AddTestFixture(GTestFixture * pTestFixture);

        // 运行指定测试用例。返回true表示没有发生FatalFailure
        bool RunTestCase(const std::wstring& testName, const std::wstring& testCaseName, OnFailuresFoundFunc onFatalFailureFound);

        // 运行指定测试用例。返回true表示没有发生FatalFailure
        bool RunTestCases(const std::vector<std::wstring>& testCaseNames, OnFailuresFoundFunc onFatalFailureFound);

        // 是否有NonFatalFailure
        bool HasNonFatalFailures(OnFailuresFoundFunc onNonFatalFailureFound) const;

        // 获取所有test的名字
        void GetTestNames(std::vector<std::wstring>& testNames) const;

        // 获取指定testName的所有testCase的名字
        void GetTestCaseNames(const std::wstring& testName, std::vector<std::wstring>& testCaseNames) const;

        // 获取指定testName和testCaseTag的所有testCase的名字
        void GetTestCaseNamesByTag(const std::wstring& testName, const std::wstring& testCaseTag, std::vector<std::wstring>& testCaseNames) const;

        // 在断言评估结束的时候
        void OnAssertEvaluated(const GAssertResult& assertResult);

       
        /// \brief 获取测试结果
        ///
        /// \return 返回测试结果
        std::wstring GetResultContext() const;

        /// \brief 测试的开始时间
        ///
        /// \return 无
        void TimerStart();

        /// \brief 测试的结束时间
        ///
        /// \return 无
        void TimerEnd();

        /// \brief 获取测试结果
        ///
        /// \param fType保存的文件类型
        /// \param filePath保存路径
        /// \return 无
        void GetResult(FileType fType, std::string filePath = "") const;

        /// \brief 获取测试结果-HTML格式
        ///
        /// \return 返回测试结果
        std::wstring GetResultAsHTML() const;
    private:
        GTestManager(void);
        GTestManager(const GTestManager& another);
        GTestManager& operator = (const GTestManager& another);

        gcmp::OwnerPtr<GTestManagerImp> m_upImp;
    };
    //////////////////////////////////////////////////////////////////////////
    // 比较器

    // 不同类型的比较
    template <typename T, typename U>
    inline bool CompareEqual(const T& lhs, const U& rhs)
    {
        return (lhs == rhs);
    }

    // 相同类型的比较
    template <typename T>
    inline bool CompareEqual(const T& lhs, const T& rhs)
    {
        return (lhs == rhs);
    }

    // 浮点数比较器
    template <typename T>
    inline bool CompareFloatEqual(T lhs, T rhs, T tol)
    {
        STATIC_ASSERT_FALSE("Type must be only float or double");
    }

    // float值比较
    template <>
    inline bool CompareFloatEqual(float lhs, float rhs, float tol)
    {
        return std::fabs(lhs - rhs) < std::fabs(tol);
    }

    // double值比较
    template <>
    inline bool CompareFloatEqual(double lhs, double rhs, double tol)
    {
        return std::fabs(lhs - rhs) < std::fabs(tol);
    }

    // 常量字符串比较器。只支持特化的指针模板，非特化类型编译报错
    template <typename T>
    inline bool CompareStrEqual(const T* lhs, const T* rhs, std::true_type)
    {
        STATIC_ASSERT_FALSE("Type must be std::string, std::wstring, const char*, or const wchar_t*");
    }

    template <typename T>
    inline bool CompareStrEqual(const T* lhs, const T* rhs, std::false_type)
    {
        STATIC_ASSERT_FALSE("Type must be std::string, std::wstring, const char*, or const wchar_t*");
    }

    // char* 比较
    template <>
    inline bool CompareStrEqual(const char * lhs, const char * rhs, std::true_type)
    {
        return (0 == gcmp::StrICmp(lhs, rhs));
    }

    template <>
    inline bool CompareStrEqual(const char * lhs, const char * rhs, std::false_type)
    {
        return (0 == std::strcmp(lhs, rhs));
    }

    // wchar_t*比较
    template <>
    inline bool CompareStrEqual(const wchar_t * lhs, const wchar_t * rhs, std::true_type)
    {
        return (0 == gcmp::StrICmp(lhs, rhs));
    }

    template <>
    inline bool CompareStrEqual(const wchar_t * lhs, const wchar_t * rhs, std::false_type)
    {
        return (0 == wcscmp(lhs, rhs));
    }

    // 常量字符串比较器。只支持特化的模板，非特化类型编译报错
    template <typename T>
    inline bool CompareStrEqual(const T& lhs, const T& rhs, std::true_type)
    {
        STATIC_ASSERT_FALSE("Type must be std::string, std::wstring, const char*, or const wchar_t*");
    }

    template <typename T>
    inline bool CompareStrEqual(const T& lhs, const T& rhs, std::false_type)
    {
        STATIC_ASSERT_FALSE("Type must be std::string, std::wstring, const char*, or const wchar_t*");
    }

    // std::string比较
    template <>
    inline bool CompareStrEqual(const std::string& lhs, const std::string& rhs, std::true_type)
    {
        return (0 == gcmp::StrICmp(lhs.c_str(), rhs.c_str()));
    }

    template <>
    inline bool CompareStrEqual(const std::string& lhs, const std::string& rhs, std::false_type)
    {
        return (0 == std::strcmp(lhs.c_str(), rhs.c_str()));
    }

    // std::wstring比较
    template <>
    inline bool CompareStrEqual(const std::wstring& lhs, const std::wstring& rhs, std::true_type)
    {
        return (0 == gcmp::StrICmp(lhs.c_str(), rhs.c_str()));
    }

    template <>
    inline bool CompareStrEqual(const std::wstring& lhs, const std::wstring& rhs, std::false_type)
    {
        return (0 == wcscmp(lhs.c_str(), rhs.c_str()));
    }

    //////////////////////////////////////////////////////////////////////////
    // 断言：
    // ASSERT_XXX为致命断言，是否为true。不成功导致整个测试终止
    // EXPECT_XXX为非致命断言，是否为true。不成功剩下测试继续

#define ASSERT_TRUE(condition) \
    { \
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"ASSERT_TRUE(" WSTRING_OF(condition) L")";\
        GAssertResult ar(GAssertResult::ART_FatalFailure, condition, GetTestName(), GetTestCaseName(), woss.str());\
        OnAssertEnd(ar);\
        if (ar.HasFatalFailure()) {\
            m_hasFatalFailure = true;\
            if(m_onFatalFailureFound!=nullptr){\
                m_onFatalFailureFound(ar);\
            }\
            return ;\
        }\
    }

#define EXPECT_TRUE(condition) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"EXPECT_TRUE(" WSTRING_OF(condition) L")";\
        GAssertResult ar(GAssertResult::ART_NonFatalFailure, condition, GetTestName(), GetTestCaseName(), woss.str());\
        OnAssertEnd(ar);\
    }

#define ASSERT_FALSE(condition) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"ASSERT_FALSE(" WSTRING_OF(condition) L")";\
        GAssertResult ar(GAssertResult::ART_FatalFailure, !(condition), GetTestName(), GetTestCaseName(), woss.str());\
        OnAssertEnd(ar);\
        if (ar.HasFatalFailure()) {\
            m_hasFatalFailure = true;\
            if(m_onFatalFailureFound!=nullptr){\
                m_onFatalFailureFound(ar);\
            }\
            return ;\
        }\
    }

#define EXPECT_FALSE(condition) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"EXPECT_FALSE(" WSTRING_OF(condition) L")";\
        GAssertResult ar(GAssertResult::ART_NonFatalFailure, !(condition), GetTestName(), GetTestCaseName(), woss.str());\
        OnAssertEnd(ar);\
    }

    template <typename T1, typename T2>
    inline GAssertResult AssertEqual(GAssertResult::EnType type, const T1& lhs, const T2& rhs, const std::wstring& test, const std::wstring& testCase, const std::wstring& assert)
    {
        const bool areEqual = CompareEqual(lhs, rhs);
        return GAssertResult(type, areEqual, test, testCase, assert);
    }

#define ASSERT_EQ(val1, val2) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"ASSERT_EQ(" WSTRING_OF(val1) L", " WSTRING_OF(val2) L")";\
        GAssertResult ar = AssertEqual(GAssertResult::ART_FatalFailure, val1, val2, GetTestName(), GetTestCaseName(), woss.str());\
        OnAssertEnd(ar);\
        if (ar.HasFatalFailure()) {\
            m_hasFatalFailure = true;\
            if(m_onFatalFailureFound!=nullptr){\
                m_onFatalFailureFound(ar);\
            }\
            return ;\
        }\
    }

#define EXPECT_EQ(val1, val2) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"EXPECT_EQ(" WSTRING_OF(val1) L", " WSTRING_OF(val2) L")";\
        GAssertResult ar = AssertEqual(GAssertResult::ART_NonFatalFailure, val1, val2, GetTestName(), GetTestCaseName(), woss.str());\
        OnAssertEnd(ar);\
    }

#define ASSERT_NE(val1, val2) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"ASSERT_NE(" WSTRING_OF(val1) L", " WSTRING_OF(val2) L")";\
        GAssertResult ar = !AssertEqual(GAssertResult::ART_FatalFailure, val1, val2, GetTestName(), GetTestCaseName(), woss.str());\
        OnAssertEnd(ar);\
        if (ar.HasFatalFailure()) {\
            m_hasFatalFailure = true;\
            if(m_onFatalFailureFound!=nullptr){\
                m_onFatalFailureFound(ar);\
            }\
            return ;\
        }\
    }

#define EXPECT_NE(val1, val2) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"EXPECT_NE(" WSTRING_OF(val1) L", " WSTRING_OF(val2) L")";\
        GAssertResult ar = !AssertEqual(GAssertResult::ART_NonFatalFailure, val1, val2, GetTestName(), GetTestCaseName(), woss.str());\
        OnAssertEnd(ar);\
    }

    template <typename T>
    inline GAssertResult AssertStrEqual(GAssertResult::EnType type, const T* lhs, const T* rhs, const std::wstring& test, const std::wstring& testCase, const std::wstring& assert, std::true_type ignoreCase)
    {
        const bool areEqual = CompareStrEqual(lhs, rhs, ignoreCase);
        return GAssertResult(type, areEqual, test, testCase, assert);
    }

    template <typename T>
    inline GAssertResult AssertStrEqual(GAssertResult::EnType type, const T* lhs, const T* rhs, const std::wstring& test, const std::wstring& testCase, const std::wstring& assert, std::false_type ignoreCase)
    {
        const bool areEqual = CompareStrEqual(lhs, rhs, ignoreCase);
        return GAssertResult(type, areEqual, test, testCase, assert);
    }

    template <typename T>
    inline GAssertResult AssertStrEqual(GAssertResult::EnType type, const T& lhs, const T& rhs, const std::wstring& test, const std::wstring& testCase, const std::wstring& assert, std::true_type ignoreCase)
    {
        const bool areEqual = CompareStrEqual(lhs, rhs, ignoreCase);
        return GAssertResult(type, areEqual, test, testCase, assert);
    }

    template <typename T>
    inline GAssertResult AssertStrEqual(GAssertResult::EnType type, const T& lhs, const T& rhs, const std::wstring& test, const std::wstring& testCase, const std::wstring& assert, std::false_type ignoreCase)
    {
        const bool areEqual = CompareStrEqual(lhs, rhs, ignoreCase);
        return GAssertResult(type, areEqual, test, testCase, assert);
    }

#define ASSERT_STREQ(str1, str2) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"ASSERT_STREQ(" WSTRING_OF(str1) L", " WSTRING_OF(str2) L")";\
        GAssertResult ar = AssertStrEqual(GAssertResult::ART_FatalFailure, str1, str2, GetTestName(), GetTestCaseName(), woss.str(), std::false_type());\
        OnAssertEnd(ar);\
        if (ar.HasFatalFailure()) {\
            m_hasFatalFailure = true;\
            if(m_onFatalFailureFound!=nullptr){\
                m_onFatalFailureFound(ar);\
            }\
            return ;\
        }\
    }

#define EXPECT_STREQ(str1, str2) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"EXPECT_STREQ(" WSTRING_OF(str1) L", " WSTRING_OF(str2) L")";\
        GAssertResult ar = AssertStrEqual(GAssertResult::ART_NonFatalFailure, str1, str2, GetTestName(), GetTestCaseName(), woss.str(), std::false_type());\
        OnAssertEnd(ar);\
    }

#define ASSERT_STRNE(str1, str2) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"ASSERT_STRNE(" WSTRING_OF(str1) L", " WSTRING_OF(str2) L")";\
        GAssertResult ar = !AssertStrEqual(GAssertResult::ART_FatalFailure, str1, str2, GetTestName(), GetTestCaseName(), woss.str(), std::false_type());\
        OnAssertEnd(ar);\
        if (ar.HasFatalFailure()) {\
            m_hasFatalFailure = true;\
            if(m_onFatalFailureFound!=nullptr){\
                m_onFatalFailureFound(ar);\
            }\
            return ;\
        }\
    }

#define EXPECT_STRNE(str1, str2) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"EXPECT_STRNE(" WSTRING_OF(str1) L", " WSTRING_OF(str2) L")";\
        GAssertResult ar = !AssertStrEqual(GAssertResult::ART_NonFatalFailure, str1, str2, GetTestName(), GetTestCaseName(), woss.str(), std::false_type());\
        OnAssertEnd(ar);\
    }

    // ignoring case
#define ASSERT_STRCASEEQ(str1, str2) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"ASSERT_STRCASEEQ(" WSTRING_OF(str1) L", " WSTRING_OF(str2) L")";\
        GAssertResult ar = AssertStrEqual(GAssertResult::ART_FatalFailure, str1, str2, GetTestName(), GetTestCaseName(), woss.str(), std::true_type());\
        OnAssertEnd(ar);\
        if (ar.HasFatalFailure()) {\
            m_hasFatalFailure = true;\
            if(m_onFatalFailureFound!=nullptr){\
                m_onFatalFailureFound(ar);\
            }\
            return ;\
        }\
    }

    // ignoring case
#define EXPECT_STRCASEEQ(str1, str2) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"EXPECT_STRCASEEQ(" WSTRING_OF(str1) L", " WSTRING_OF(str2) L")";\
        GAssertResult ar = AssertStrEqual(GAssertResult::ART_NonFatalFailure, str1, str2, GetTestName(), GetTestCaseName(), woss.str(), std::true_type());\
        OnAssertEnd(ar);\
    }

    // ignoring case
#define ASSERT_STRCASENE(str1, str2) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"ASSERT_STRCASENE(" WSTRING_OF(str1) L", " WSTRING_OF(str2) L")";\
        GAssertResult ar = !AssertStrEqual(GAssertResult::ART_FatalFailure, str1, str2, GetTestName(), GetTestCaseName(), woss.str(), std::true_type());\
        OnAssertEnd(ar);\
        if (ar.HasFatalFailure()) {\
            m_hasFatalFailure = true;\
            if(m_onFatalFailureFound!=nullptr){\
                m_onFatalFailureFound(ar);\
            }\
            return ;\
        }\
    }

    // ignoring case
#define EXPECT_STRCASENE(str1, str2) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"EXPECT_STRCASENE(" WSTRING_OF(str1) L", " WSTRING_OF(str2) L")";\
        GAssertResult ar = !AssertStrEqual(GAssertResult::ART_NonFatalFailure, str1, str2, GetTestName(), GetTestCaseName(), woss.str(), std::true_type());\
        OnAssertEnd(ar);\
    }

    template <typename T>
    inline GAssertResult AssertFloatEqual(GAssertResult::EnType type, T lhs, T rhs, T tol, const std::wstring& test, const std::wstring& testCase, const std::wstring& assert)
    {
        const bool areEqual = CompareFloatEqual(lhs, rhs, tol);
        return GAssertResult(type, areEqual, test, testCase, assert);
    }

#define ASSERT_FLOAT_EQ(val1, val2, tol) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"ASSERT_FLOAT_EQ(" WSTRING_OF(val1) L", " WSTRING_OF(val2) L", " WSTRING_OF(tol) L")";\
        GAssertResult ar = AssertFloatEqual(GAssertResult::ART_FatalFailure, val1, val2, tol, GetTestName(), GetTestCaseName(), woss.str());\
        OnAssertEnd(ar);\
        if (ar.HasFatalFailure()) {\
            m_hasFatalFailure = true;\
            if(m_onFatalFailureFound!=nullptr){\
                m_onFatalFailureFound(ar);\
            }\
            return ;\
        }\
    }

#define EXPECT_FLOAT_EQ(val1, val2, tol) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"EXPECT_FLOAT_EQ(" WSTRING_OF(val1) L", " WSTRING_OF(val2) L", " WSTRING_OF(tol) L")";\
        GAssertResult ar = AssertFloatEqual(GAssertResult::ART_NonFatalFailure, val1, val2, tol, GetTestName(), GetTestCaseName(), woss.str());\
        OnAssertEnd(ar);\
    }

#define ASSERT_DOUBLE_EQ(val1, val2, tol) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"ASSERT_DOUBLE_EQ(" WSTRING_OF(val1) L", " WSTRING_OF(val2) L", " WSTRING_OF(tol) L")";\
        GAssertResult ar = AssertFloatEqual(GAssertResult::ART_FatalFailure, val1, val2, tol, GetTestName(), GetTestCaseName(), woss.str());\
        OnAssertEnd(ar);\
        if (ar.HasFatalFailure()) {\
            m_hasFatalFailure = true;\
            if(m_onFatalFailureFound!=nullptr){\
                m_onFatalFailureFound(ar);\
            }\
            return ;\
        }\
    }

#define EXPECT_DOUBLE_EQ(val1, val2, tol) \
    {\
        std::wostringstream woss;\
        woss << __FILE__ << L"(" << __LINE__ << L"): "<< L"EXPECT_DOUBLE_EQ(" WSTRING_OF(val1) L", " WSTRING_OF(val2) L", " WSTRING_OF(tol) L")";\
        GAssertResult ar = AssertFloatEqual(GAssertResult::ART_NonFatalFailure, val1, val2, tol, GetTestName(), GetTestCaseName(), woss.str());\
        OnAssertEnd(ar);\
    }
}

#pragma warning(pop)
